<?php
namespace Monkey;

use MonkeyException;

/**
 * 响应类\Monkey\Response
 * @package    Monkey
 * @author     HuangYi
 * @copyright  Copyright (c) 2011-07-01——2013-03-30
 * @license    New BSD License
 * @version    $Id: \Monkey\Response.php 版本号 2013-03-30 $
 *
 * 相应状态码信息描述：
 * 1xx：信息，请求收到，继续处理
 * 2xx：成功，行为被成功地接受、理解和采纳
 * 3xx：重定向，为了完成请求，必须进一步执行的动作
 * 4xx：客户端错误，请求包含语法错误或者请求无法实现
 * 5xx：服务器错误，服务器不能实现一种明显无效的请求
 */
class Response
{
    const W_CONTINUE = 100;
    const W_SWITCHING_PROTOCOLS = 101;
    const W_OK = 200;
    const W_CREATED = 201;
    const W_ACCEPTED = 202;
    const W_NON_AUTHORITATIVE_INFORMATION = 203;
    const W_NO_CONTENT = 204;
    const W_RESET_CONTENT = 205;
    const W_PARTIAL_CONTENT = 206;
    const W_MULTIPLE_CHOICES = 300;
    const W_MOVED_PERMANENTLY = 301;
    const W_MOVED_TEMPORARILY = 302;
    const W_FOUND = 302;
    const W_SEE_OTHER = 303;
    const W_NOT_MODIFIED = 304;
    const W_USE_PROXY = 305;
    const W_TEMPORARY_REDIRECT = 307;
    const W_BAD_REQUEST = 400;
    const W_UNAUTHORIZED = 401;
    const W_PAYMENT_REQUIRED = 402;
    const W_FORBIDDEN = 403;
    const W_NOT_FOUND = 404;
    const W_METHOD_NOT_ALLOWED = 405;
    const W_NOT_ACCEPTABLE = 406;
    const W_PROXY_AUTHENTICATION_REQUIRED = 407;
    const W_REQUEST_TIMEOUT = 408;
    const W_CONFLICT = 409;
    const W_GONE = 410;
    const W_LENGTH_REQUIRED = 411;
    const W_PRECONDITION_FAILED = 412;
    const W_REQUEST_ENTITY_TOO_LARGE = 413;
    const W_REQUEST_URI_TOO_LONG = 414;
    const W_UNSUPPORTED_MEDIA_TYPE = 415;
    const W_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
    const W_EXPECTATION_FAILED = 417;
    const W_INTERNAL_SERVER_ERROR = 500;
    const W_NOT_IMPLEMENTED = 501;
    const W_BAD_GATEWAY = 502;
    const W_SERVICE_UNAVAILABLE = 503;
    const W_GATEWAY_TIMEOUT = 504;
    const W_HTTP_VERSION_NOT_SUPPORTED = 505;
    private $_bodyIndex = array();//保存模板名字的顺序索引
    private $_headers = array();//设置输出的头部信息
    private $_isRedirect = false;//是否直接跳转
    private $_status = '';//设置相应状态码
    private $_bodys = array();//用以保存响应内容
    private $_charset;//输出的编码
    private static $_instance;
    private function __construct(){ }
    public static function _instance(){
        !self::$_instance and self::$_instance=new self();
        return self::$_instance;
    }
    /**
     * 设置响应头信息，如果已经设置过同名的响应头，该方法将用新的设置取代原来的头字段
     * @param string $name 响应头的名称
     * @param string $value 响应头的字段取值
     * @return $this
     */
    public function addHeader($name, $value) {
        $name=ucwords(strtolower($name));
        $this->_headers[$name]=$value;
        return $this;
    }
    /**
     * 设置响应头状态码
     * @param int $status 响应状态码
     * @return $this
     */
    public function setStatus($status) {
        $status = intval($status);
        if ($status < 100 || $status > 505) return $this;
        $this->_status = (int) $status;
        return $this;
    }
    /**
     * 设置响应内容
     * @param string $content 响应内容信息
     * @param string $name 相应内容片段名字,默认为null
     * @return $this;
     */
    public function setBody($content, $name = 'default') {
        if (!$content || !$name) return $this;
        array_push($this->_bodyIndex, $name);
        $this->_bodys[$name] = $content;
        return $this;
    }
    /**
     * 重定向一个响应信息
     * @param string $url 重定向的地址
     * @param int $status 状态码,默认为302
     * @return void
     */
    public function redirect($url, $status = 302) {
        if (!is_int($status) || $status < 300 || $status > 399) return;
        $this->_isRedirect = true;
        $this->addHeader('Location', $url)
            ->setStatus($status)
            ->sendHeaders()
            ->End();
    }
    /**
     * 发送一个错误的响应信息
     * @param int $status 错误码,默认为404
     * @param string $message 错误信息,默认为空
     * @return $this
     */
    public function sendError($status = self::W_NOT_FOUND, $message = '') {
        if (!is_int($status) || $status < 400 || $status > 505) return $this;
        $this->setBody($message, 'error');
        $this->setStatus($status);
        return $this->send();
    }
    /**
     * 发送响应信息
     * 依次发送响应头和响应内容
     * @return $this
     */
    public function send() {
        return $this->sendHeaders()->sendBody();
    }
    /**
     * 发送响应头部信息
     * @return $this
     */
    public function sendHeaders() {
        if ($this->isSendedHeader()) return $this;
        if($this->_headers['Content-type'] && $this->_charset){
            $type=explode(';',$this->_headers['Content-type']);
            if(!isset($type[1])) $this->_headers['Content-type']=$type[0].'; '.$this->_charset;
        }
        foreach ($this->_headers as $name => $value) {
            header($name . ': ' . $value);
        }
        if ($this->_status) {
            header('HTTP/1.x ' . $this->_status . ' ' . $this->statusMap($this->_status));
            header('Status: ' . $this->_status . ' ' . $this->statusMap($this->_status));
        }
        return $this;
    }
    /**
     * 发送响应内容
	 * @return $this
     */
    public function sendBody() {
        foreach ($this->_bodyIndex as $key)
            echo $this->_bodys[$key];
        return $this;
    }
    /**
     * 是否已经发送了响应头部
     * @param boolean $throw 如果已经发送了头部是否抛出错误,默认为false
     * @return boolean 已经发送头部信息则返回true否则返回false
     */
    public function isSendedHeader($throw = false) {
        $sended = headers_sent($file, $line);
        if ($throw && $sended)
            MonkeyException::halt('Headers 已经发送，文件：' . $file . '； 行号：' . $line,2048,__FILE__,__LINE__);
        return $sended;
    }
    /**
     * 获取响应头信息
     * @return array
     */
    public function getHeaders() {
        return $this->_headers;
    }
    /**
     * 清理响应体信息
     * @return $this
     */
    public function clearBody() {
        $this->_bodys = array();
        return $this;
    }
    /**
     * 清除响应头信息
     * @return $this
     */
    public function clearHeaders() {
        $this->_headers = array();
        return $this;
    }
    /**
	 * 获得输出的编码方式
	 * @return string
     */
    public function getCharset() {
        return $this->_charset;
    }
    /**
     * 设置输出的编码方式
     * @param string $_charset 编码方式
     * @return $this
     */
    public function setCharset($_charset='UTF-8') {
        $this->_charset = $_charset;
        return $this;
    }
    /**
     * 返回状态码说明
     * @param $code
     * @return string
     */
    public function statusMap($code) {
        static $maps = array(
            100 => 'Continue',
            101 => 'Switching Protocols',
            200 => 'OK',
            201 => 'Created',
            202 => 'Accepted',
            203 => 'Non-Authoritative Information',
            204 => 'No Content',
            205 => 'Reset Content',
            206 => 'Partial Content',
            207 => 'Multi-Status',
            300 => 'Multiple Choices',
            301 => 'Moved Permanently',
            302 => 'Found',
            303 => 'See Other',
            304 => 'Not Modified',
            305 => 'Use Proxy',
            307 => 'Temporary Redirect',
            400 => 'Bad Request',
            401 => 'Unauthorized',
            402 => 'Payment Required',
            403 => 'Forbidden',
            404 => 'Not Found',
            405 => 'Method Not Allowed',
            406 => 'Not Acceptable',
            407 => 'Proxy Authentication Required',
            408 => 'Request Timeout',
            409 => 'Conflict',
            410 => 'Gone',
            411 => 'Length Required',
            412 => 'Precondition Failed',
            413 => 'Request Entity Too Large',
            414 => 'Request-URI Too Long',
            415 => 'Unsupported Media Type',
            416 => 'Requested Range Not Satisfiable',
            417 => 'Expectation Failed',
            422 => 'Unprocessable Entity',
            423 => 'Locked',
            424 => 'Failed Dependency',
            500 => 'Internal Server Error',
            501 => 'Not Implemented',
            502 => 'Bad Gateway',
            503 => 'Service Unavailable',
            504 => 'Gateway Timeout',
            505 => 'HTTP Version Not Supported',
            507 => 'Insufficient Storage',
            509 => 'Bandwidth Limit Exceeded'
        );
        return isset($maps[$code]) ? $maps[$code] : '';
    }
    /**
     * 结束响应
     */
    public function end(){
        exit;
    }
}
